home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume18 / mush6.4 / part15 < prev    next >
Encoding:
Internet Message Format  |  1989-03-12  |  33.6 KB

  1. Subject:  v18i037:  Mail user's shell version 6.4, Part15/19
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Dan Heller <island!argv@sun.com>
  7. Posting-number: Volume 18, Issue 37
  8. Archive-name: mush6.4/part15
  9.  
  10.  
  11.  
  12. #! /bin/sh
  13. # This is a shell archive.  Remove anything before this line, then unpack
  14. # it by saving it into a file and typing "sh file".  To overwrite existing
  15. # files, type "sh file -c".  You can also feed this as standard input via
  16. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  17. # will see the following message at the end:
  18. #        "End of archive 15 (of 19)."
  19. # Contents:  addrs.c
  20. # Wrapped by rsalz@papaya.bbn.com on Mon Mar 13 19:25:21 1989
  21. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  22. if test -f 'addrs.c' -a "${1}" != "-c" ; then 
  23.   echo shar: Will not clobber existing file \"'addrs.c'\"
  24. else
  25. echo shar: Extracting \"'addrs.c'\" \(31712 characters\)
  26. sed "s/^X//" >'addrs.c' <<'END_OF_FILE'
  27. X/* addrs.c -- copyright (c) Dan Heller 1/25/1989 */
  28. X
  29. X#include "mush.h"
  30. X
  31. X/*
  32. X * Check to see if all addressees in list1 is in list2.
  33. X * The lists must be as clean as the driven snow (no comments, aliases
  34. X * must have been expanded, all are separated by commas or whitespace.
  35. X *
  36. X * "user" matches "user" and "user@localhost"
  37. X * "*user" matches "user" at any address whatsoever."
  38. X * !host matches any user destined for the specified host.
  39. X * !some!path is the same, but can be more specifiec in the path.
  40. X * @dom.ain can match any user destined for any host within the domain.
  41. X *      @berkeley.edu would match: dheller@cory.berkeley.edu
  42. X */
  43. Xcompare_addrs(list1, list2, ret_buf)
  44. Xchar *list1, *list2, ret_buf[];
  45. X{
  46. X    register char    *p;
  47. X    char        **addrv, **listv, buf[256]; /* addrs aren't long */
  48. X    int            addrc, listc, a, l, h, ret_val;
  49. X
  50. X    /* autosign2 list contains non-comment addresses */
  51. X    listv = mk_argv(list1, &listc, FALSE);
  52. X    addrv = mk_argv(list2, &addrc, FALSE);
  53. X
  54. X    /* loop thru both lists and convert addresses to !-format
  55. X     * then remove ourhost names so "user" matches "user!local"
  56. X     * also remove possible trailing commas (from list).
  57. X     */
  58. X    for (a = 0; a < addrc; a++) {
  59. X    if (a != addrc-1 && (p = index(addrv[a], ',')) && !p[1])
  60. X        *p = 0;
  61. X    if (addrv[a][0] == '!' || addrv[a][0] == '@')
  62. X        continue;
  63. X    (void) bang_form(buf, addrv[a]);
  64. X    if (strcmp(addrv[a], buf)) /* if they differ... */
  65. X        (void) strcpy(addrv[a], buf); /* save new version */
  66. X    }
  67. X    for (l = 0; l < listc; l++) {
  68. X    if (l != listc-1 && (p = index(listv[l], ',')) && !p[1])
  69. X        *p = 0;
  70. X    if (listv[l][0] == '!' || listv[l][0] == '@')
  71. X        continue;
  72. X    (void) bang_form(buf, listv[l]);
  73. X    if (strcmp(listv[l], buf)) /* if they differ... */
  74. X        (void) strdup(listv[l], buf); /* save new version */
  75. X    }
  76. X
  77. X    Debug("\nlist1 = "), print_argv(listv);
  78. X    Debug("list2 = "), print_argv(addrv), putchar('\n');
  79. X
  80. X    /* loop thru each list comparing each element with the
  81. X     * other, if necessary.
  82. X     */
  83. X    for (l = 0; l < listc; l++) {
  84. X    ret_val = 0;
  85. X    /* check if local recipient with was specified. */
  86. X    if (!(p = rindex(listv[l], '!')))
  87. X        for (a = 0; a < addrc; a++) {
  88. X        /* we have a local user so far.  If addrv[] is
  89. X         * not remote, then strcmp() immediately.
  90. X         * Note that "!" with no host indicates *all*
  91. X         * local users!!!
  92. X         */
  93. X        if (addrv[a][0] != '!') {
  94. X           if (!lcase_strncmp(addrv[a], listv[l], -1) || !addrv[a][1])
  95. X            ret_val = 1;
  96. X        } else if (addrv[a][0] == '*') {
  97. X            /* "*user" == "user" or "*" == login */
  98. X            if (!addrv[a][1] && !lcase_strncmp(listv[l], login) ||
  99. X            !lcase_strncmp(listv[l], addrv[a]+1, -1))
  100. X            ret_val = 1;
  101. X        } else for (h = 0; ourname && ourname[h]; h++)
  102. X            if (!lcase_strncmp(addrv[a]+1,
  103. X            ourname[h], -1)) {
  104. X            ret_val = 1;
  105. X            break;
  106. X            }
  107. X        if (ret_val)
  108. X            break;
  109. X        }
  110. X    /* else this is a remote user */
  111. X    else {
  112. X        /* check all the addresses for @dom.ain stuff or
  113. X         * !path!name type stuff only.
  114. X         */
  115. X        /* first back up p to the previous '!' */
  116. X        char *start, *user = p + 1;
  117. X        while (p-1 >= listv[l] && *--p != '!')
  118. X        ;
  119. X        start = p; /* Where to start for _domain_ addrs */
  120. X        for (a = 0; a < addrc; a++) {
  121. X        int len;
  122. X        char *path;
  123. X
  124. X        /* first check the cases of address unmodified by @ and !
  125. X         * or check to see if  *user  is specified.
  126. X         */ 
  127. X        if (addrv[a][0] != '@' && addrv[a][0] != '!') {
  128. X            if (addrv[a][0] == '*') {
  129. X            /* we saved the username at "user" declaration. */
  130. X            /* if "*" is by itself, check against user's login */
  131. X            if (!addrv[a][1] && !lcase_strncmp(user, login, -1) ||
  132. X                addrv[a][1] && !lcase_strncmp(user, addrv[a]+1,-1)){
  133. X                ret_val = 1;
  134. X                break;
  135. X            }
  136. X            } else if (!lcase_strncmp(addrv[a], listv[l], -1)) {
  137. X            ret_val = 1;
  138. X            break;
  139. X            }
  140. X            continue;
  141. X        }
  142. X        path = addrv[a]+1;
  143. X        while (addrv[a][1] == '@' && *path == '.')
  144. X            path++;
  145. X        if ((len = strlen(path)) == 0)
  146. X            continue; /* localhost stuff only -- can't match */
  147. X        /* first check against specified domains */
  148. X        if (addrv[a][0] == '@') {
  149. X            for (p = start; p; (p = index(p, '.')) && ++p)
  150. X            if (!lcase_strncmp(p, path, len) &&
  151. X                (p[len] == '.' || p[len] == 0 || p[len] == '!')) {
  152. X                ret_val = 1;
  153. X                break;
  154. X            }
  155. X        } else if (addrv[a][0] == '!') {
  156. X            /* for !path style, start at head of addr */
  157. X            for (p = listv[l]; p; (p = index(p, '!')) && ++p)
  158. X            if (!lcase_strncmp(p, path, len) &&
  159. X                (p[len] == '!' || p[len] == 0)) {
  160. X                ret_val = 1;
  161. X                break;
  162. X            }
  163. X        }
  164. X        /* If address is in autosign2, goto next addr */
  165. X        if (ret_val)
  166. X            break;
  167. X        }
  168. X    }
  169. X    if (!ret_val) {
  170. X        /* this address isn't in autosign2 list */
  171. X        if (ret_buf)
  172. X        (void) strcpy(ret_buf, listv[l]);
  173. X        break;
  174. X    }
  175. X    }
  176. X    free_vec(listv);
  177. X    free_vec(addrv);
  178. X
  179. X    return ret_val;
  180. X}
  181. X
  182. X/*
  183. X * Parser for stupidly-formed RFC822 addresses.  It has been tested on
  184. X * several bizzare cases as well as the normal stuff and uucp paths.  It
  185. X * takes a string which is a bunch of addresses and unscrambles the first
  186. X * one in the string.  It returns a pointer to the first char past what it
  187. X * unscrambled and copies the unscrambled address to its second argument.
  188. X * 
  189. X * It does NOT deal with trailing (comment) strings --
  190. X *         <whoever@somewhere> (This is a comment)
  191. X *                            ^unscramble_addr return points here
  192. X * 
  193. X * It also does not deal well with malformed <addresses> --
  194. X *         <whoever@somewhere,nowhere>
  195. X *                           ^unscramble_addr return points here
  196. X * 
  197. X * In each of the above cases, the string "whoever@somewhere" is copied
  198. X * to the second argument.
  199. X * 
  200. X * Nothing is done to un-<>ed route-less RFC822/976 addresses, nor to
  201. X * uucp paths, nor to mixed-mode addresses not containing a route.
  202. X * Hopelessly scrambled addresses are not handled brilliantly --
  203. X *     @some.dumb.place,@any.other.place:sys2!user%sys3@sys1
  204. X * parses to
  205. X *     sys2!user%sys3@sys1
  206. X * i.e., the route is simply dropped.
  207. X */
  208. Xchar *
  209. Xunscramble_addr(addr, naddr)
  210. Xchar *addr;
  211. Xchar *naddr;
  212. X{
  213. X    char *i, *r, *at;
  214. X    char s[BUFSIZ], t[BUFSIZ];
  215. X    int anglebrace = 0;
  216. X
  217. X    /* Make a copy of the address so we can mangle it freely. */
  218. X    if (addr && *addr) {
  219. X    /* Skip any leading whitespace. */
  220. X    for (i = addr; *i && (r = any(i, " \t")) == i;)
  221. X        if (r) i = r + 1;
  222. X    if (*i == '\0')
  223. X        return NULL;
  224. X    /* Skip any leading double-quoted comment. */
  225. X    if (*i == '"') {
  226. X        if ((i = index(i + 1, '"')) && (*i == '\0' || *(++i) == '\0'))
  227. X            return NULL;
  228. X    }
  229. X    /* Skip any more whitespace. */
  230. X    for (; *i && (r = any(i, " \t")) == i;)
  231. X        if (r) i = r + 1;
  232. X    if (*i == '\0')
  233. X        return NULL;
  234. X    /* Check for angle braces around the address. */
  235. X    if (*i == '<') {
  236. X        if (*(++i) == '\0')
  237. X        return NULL;
  238. X        ++anglebrace;
  239. X    }
  240. X    /*
  241. X     * Look for a route.  A route is a comma-separated set of @-tagged
  242. X     *  domains terminated by a colon.  Later versions might try to use
  243. X     *  the route, but for now it confuses too many mailers.
  244. X     */
  245. X    if ((*i == '@') && (r = any(i, " \t:"))) {
  246. X        if (*r != ':')
  247. X        return NULL;
  248. X        if (*(r + 1) == '\0')
  249. X        return NULL;
  250. X        /*
  251. X         * Back up to the rightmost @-tagged domain
  252. X         *  (see note below about unwinding)
  253. X         */
  254. X        *r = '\0';
  255. X        i = rindex(i, '@');
  256. X        *r = ':';
  257. X    }
  258. X    /* Remember how much we've skipped, and copy the rest. */
  259. X    at = i;
  260. X    (void) strcpy(t,i);
  261. X    /* Strip from a trailing angle brace, if present. */
  262. X    if (anglebrace) {
  263. X        if (r = any(t, "> \t")) {
  264. X        if (r == t || *r != '>')
  265. X            return NULL;
  266. X        else
  267. X            *r = '\0';
  268. X        --anglebrace;
  269. X        } else
  270. X        return NULL;
  271. X    }
  272. X    if (t[0] == '@') {
  273. X        /* Chop off any invalid stuff after the address. */
  274. X        if (r = any(index(t, ':'), " \t,(<"))
  275. X        *r = '\0';
  276. X    }
  277. X    } else
  278. X    return NULL;
  279. X    /* Remember where we are so we can return it. */
  280. X    at += strlen(t) + 1;
  281. X    /*
  282. X     * Unscramble the route, if present.
  283. X     *  NOTE:  We assume that a route is present in only two cases:
  284. X     *   1) addr was taken from the "From " line of a stupid mailer
  285. X     *   2) addr was a well-formed, <> enclosed RFC822 address
  286. X     */
  287. X    if (t[0] == '@') {
  288. X    if (r = index(t, ':'))
  289. X        r++;
  290. X    else
  291. X        return NULL;
  292. X    /* Delete the route if extraneous, otherwise unwind it. */
  293. X    if (i = index(r, '@'))
  294. X        (void) strcpy(s, r);
  295. X    else {
  296. X        /*
  297. X         * NOTE:  Unwinding currently uses only the rightmost domain
  298. X         *  in the route.  This will break for mailers that need the
  299. X         *  entire route.  Complete unwinding would require the use
  300. X         *  of % characters, which are avoided for other reasons.
  301. X         */
  302. X        (void) strcpy(s, r);
  303. X        *(--r) = '\0';
  304. X        (void) strcat(s, t);
  305. X    }
  306. X    } else
  307. X    (void) strcpy(s, t);
  308. X    /*
  309. X     * Ok, now the address should be in the form user@domain and
  310. X     *  is held in buffer s (t[] is not copied directly to naddr
  311. X     *  to allow future additional processing to be added here).
  312. X     */
  313. X    if (debug > 1) /* Don't dump this on trivial debugging */
  314. X    print("Converting \"%s\" to \"%s\"\n", addr, s);
  315. X    (void) strcpy(naddr, s);
  316. X    return at;
  317. X}
  318. X
  319. X/*
  320. X * Convert RFC822 or mixed addresses to RFC976 `!' form,
  321. X *  copying the new address to d.  The source address is
  322. X *  translated according to RFC822 rules.
  323. X * Return a pointer to the end (nul terminus) of d.
  324. X */
  325. Xchar *
  326. Xbang_form (d, s)
  327. Xchar *d, *s;
  328. X{
  329. X    char *r, *t, *ab = NULL;
  330. X
  331. X    *d = '\0';
  332. X    /* If nothing to do, quit now */
  333. X    if (!s || !*s) {
  334. X    return d;
  335. X    }
  336. X    /* Avoid any angle braces */
  337. X    if (*s == '<') {
  338. X    if (ab = index(s + 1, '>'))
  339. X        s++, *ab = '\0';
  340. X    else
  341. X        return NULL;
  342. X    }
  343. X    /*
  344. X     * Look backwards for the first `@'; this gives us the
  345. X     * primary domain of the RFC822 address
  346. X     */
  347. X    if ((t = rindex(s, '@')) && t != s) {
  348. X    /* Copy the RFC822 domain as the UUCP head */
  349. X    d += Strcpy(d, t + 1);
  350. X    *d++ = '!';
  351. X    *t = '\0';
  352. X    r = bang_form(d, s);
  353. X    *t = '@';
  354. X    } else if (*s == '@') {
  355. X    /* An RFC-822 "@domain1,@domain2:" routing */
  356. X    if (t = any(++s, ",:")) {
  357. X        char c = *t;
  358. X        *t = '\0';
  359. X        d += Strcpy(d, s);
  360. X        *d++ = '!';
  361. X        *t++ = c;
  362. X        r = bang_form(d, t);
  363. X    } else
  364. X        r = NULL;
  365. X    } else if (t = index(s, '!')) {
  366. X    /* A normal UUCP path */
  367. X    *t = '\0';
  368. X    d += Strcpy(d, s);
  369. X    *t++ = *d++ = '!';
  370. X    r = bang_form(d, t);
  371. X    } else if (t = rindex(s, '%')) {
  372. X    /* An imbedded `%' -- treat as low-priority `@' */
  373. X    *t = '@';
  374. X    r = bang_form(d, s);
  375. X    *t = '%';
  376. X    } else
  377. X    r = d + Strcpy(d, s);  /* No `@', `!', or `%' */
  378. X    if (ab)
  379. X    *ab = '>';
  380. X    return r;
  381. X}
  382. X
  383. X/*
  384. X * Route addresses according to certain criteria.  This function is really
  385. X * just a front end for improve_uucp_paths() which does routing (differently).
  386. X * If "route" is null, this routine is being called incorrectly.
  387. X * If route is an address, just call improve_uucp_paths() and return.
  388. X * If route is the null string, then route all addresses via the sender's
  389. X * which is the first name/address on the To: list. If he's on a remote
  390. X * machine, chances are that the addresses of everyone else he mailed to
  391. X * are addresses from his machine.  Reconstruct those addresses to route
  392. X * thru the senders machine first.
  393. X */
  394. Xroute_addresses(to, cc, route_path)
  395. Xchar *to, *cc, *route_path;
  396. X{
  397. X    char pre_path[256], addr[256];
  398. X    register char *next, *p;
  399. X    int pre_len = 0;
  400. X
  401. X    if (!route_path)
  402. X    return;
  403. X    if (*route_path) {
  404. X    improve_uucp_paths(to, HDRSIZ, route_path);
  405. X    improve_uucp_paths(cc, HDRSIZ, route_path);
  406. X    return;
  407. X    }
  408. X
  409. X    pre_path[0] = 0;
  410. X    /* Get the address of the sender (which is always listed first) */
  411. X    if (!(next = get_name_n_addr(to, NULL, addr)))
  412. X    return;
  413. X    /* check to see if there is only one addr on To: line and no Cc: header */
  414. X    if (!*next && (!cc || !*cc))
  415. X    return;
  416. X
  417. X    /* fix up the sender's address; improve_uucp_paths to optimize pre_path */
  418. X    improve_uucp_paths(addr, sizeof addr, NULL);
  419. X
  420. X    if (p = rindex(addr, '!')) {
  421. X    *p = 0;
  422. X    pre_len = Strcpy(pre_path, addr); /* the uucp route he used */
  423. X    Debug("Routing thru \"%s\"\n", pre_path);
  424. X    }
  425. X
  426. X    while (*next == ',' || isspace(*next))
  427. X     next++;
  428. X    improve_uucp_paths(next, HDRSIZ - (int)(next - to), pre_path);
  429. X    improve_uucp_paths(cc, HDRSIZ, pre_path);
  430. X}
  431. X
  432. X/*
  433. X * pass a string describing header like, "Subject: ", current value, and
  434. X * whether or not to prompt for it or to just post the information.
  435. X * If do_prompt is true, "type in" the current value so user can either
  436. X * modify it, erase it, or add to it.
  437. X */
  438. Xchar *
  439. Xset_header(str, curstr, do_prompt)
  440. Xregister char *str, *curstr;
  441. X{
  442. X    static char       buf[HDRSIZ];
  443. X    int        offset = 0;
  444. X    register char  *p = curstr;
  445. X
  446. X    if (!str)
  447. X    str = "";
  448. X
  449. X    buf[0] = 0;
  450. X    wprint(str);
  451. X    fflush(stdout);         /* force str curstr */
  452. X    if (do_prompt) {
  453. X    if (curstr)
  454. X#ifdef SUNTOOL
  455. X        if (istool)
  456. X        for (p = curstr; *p; p++)
  457. X            rite(*p); /* mimics typing for the tool */
  458. X        else
  459. X#endif /* SUNTOOL */
  460. X        if (isoff(glob_flags, ECHO_FLAG)) {
  461. X        Ungetstr(curstr);
  462. X        } else
  463. X#ifdef TIOCSTI
  464. X        for (p = curstr; *p; p++)
  465. X            if (ioctl(0, TIOCSTI, p) == -1) {
  466. X            error("ioctl: TIOCSTI");
  467. X            wprint("You must retype the entire line.\n%s", str);
  468. X            break;
  469. X            }
  470. X#else /* !TIOCSTI */
  471. X        wprint("WARNING: -e flag! Type the line over.\n%s", str);
  472. X#endif /* TIOCSTI */
  473. X
  474. X    if (istool)
  475. X        return NULL;
  476. X    /* simulate the fact that we're getting input for the letter even tho
  477. X     * we may not be.  set_header is called before IS_GETTING is true,
  478. X     * but if we set it to true temporarily, then signals will return to
  479. X     * the right place (stop/continue).
  480. X     */
  481. X    {
  482. X        u_long getting = ison(glob_flags, IS_GETTING);
  483. X        int wrapping = wrapcolumn;
  484. X        /* Funky trick here.  If the prompt string is empty,
  485. X         * assume that we are allowed to do line wrap;
  486. X         * otherwise, temporarily disable line wrap
  487. X         */
  488. X        if (*str)
  489. X        wrapcolumn = 0;
  490. X        if (!getting)
  491. X        turnon(glob_flags, IS_GETTING);
  492. X        if (Getstr(buf, sizeof(buf), offset) == -1) {
  493. X        putchar('\n');
  494. X        buf[0] = 0;
  495. X        }
  496. X        if (!getting)
  497. X        turnoff(glob_flags, IS_GETTING);
  498. X        wrapcolumn = wrapping;
  499. X    }
  500. X    } else
  501. X    puts(strcpy(buf, curstr));
  502. X    if (debug > 1)
  503. X    print("returning (%s) from set_header\n", buf);
  504. X    return buf;
  505. X}
  506. X
  507. X/*
  508. X * improve uucp paths by looking at the name of each host listed in the
  509. X * path given.
  510. X *    sun!island!pixar!island!argv
  511. X * It's a legal address, but redundant. Also, if we know we talk to particular
  512. X * hosts via uucp, then we can just start with that host and disregard the path
  513. X * preceding it.  So, first get the known hosts and save them. Then start
  514. X * at the end of the original path (at the last ! found), and move backwards
  515. X * saving each hostname.  If we get to a host that we know about, stop there
  516. X * and use that address.  If we get to a host we've already seen, then
  517. X * delete it and all the hosts since then until the first occurrence of that
  518. X * hostname.  When we get to the beginning, the address will be complete.
  519. X * The route_path is prepended to each address to check make sure this path
  520. X * is used if no known_hosts precede it in that address.
  521. X *
  522. X * Return all results into the original buffer passed to us.  If route_path
  523. X * adds to the length of all the paths, then the original buffer could be
  524. X * overwritten.  someone should check for this!
  525. X */
  526. Ximprove_uucp_paths(original, size, route_path)
  527. Xchar *original, *route_path;
  528. X{
  529. X    char       name[256], addr[256], buf[2 * HDRSIZ], *end;
  530. X    char      *hostnames[32], tmp[sizeof addr];
  531. X    register char *p, *p2, *recipient, *start = original, *b = buf;
  532. X    int           saved_hosts, i;
  533. X
  534. X    if (!original || !*original)
  535. X    return;
  536. X
  537. X    while (end = get_name_n_addr(start, name, tmp)) {
  538. X    /* first copy the route path, then the rest of the address. */
  539. X    p = addr;
  540. X    if (route_path && *route_path) {
  541. X        p += Strcpy(addr, route_path);
  542. X        *p++ = '!';
  543. X    }
  544. X    (void) bang_form(p, tmp);
  545. X    saved_hosts = 0;
  546. X    if (p2 = rindex(addr, '!')) {
  547. X        recipient = p2+1;
  548. X        /* save the uucp-style address *without* route_path in tmp */
  549. X        (void) strcpy(tmp, p);
  550. X        for (p = p2; p > addr; p--) {
  551. X        /* null the '!' separating the rest of the path from the part
  552. X         * of the path preceding it and move p back to the previous
  553. X         * '!' (or beginning to addr) for hostname to point to.
  554. X         */
  555. X        for (*p-- = 0; p > addr && *p != '!'; p--)
  556. X            ;
  557. X        /* if p is not at the addr, move it forward past the '!' */
  558. X        if (p != addr)
  559. X            ++p; /* now points to a null terminated hostname */
  560. X        /* if host is ourselves, ignore this and preceding hosts */
  561. X        for (i = 0; ourname && ourname[i]; i++)
  562. X            if (!lcase_strncmp(p, ourname[i], -1))
  563. X            break;
  564. X        if (ourname && ourname[i])
  565. X            break;
  566. X        /* check already saved hostnames. If host is one of them,
  567. X         * delete remaining hostnames since there is a redundant path.
  568. X         */
  569. X        for (i = 0; i < saved_hosts; i++)
  570. X            if (!lcase_strncmp(hostnames[i], p, -1))
  571. X                saved_hosts = i;
  572. X
  573. X        hostnames[saved_hosts++] = p;
  574. X        if (p == addr)
  575. X            break;
  576. X        /* If we know that we call this host, break */
  577. X        for (i = 0; known_hosts && known_hosts[i]; i++)
  578. X            if (!lcase_strncmp(p, known_hosts[i], -1))
  579. X            break;
  580. X        }
  581. X        /* temporary holder for where we are in buffer (save address) */
  582. X        p2 = b;
  583. X        while (saved_hosts-- > 0) {
  584. X        b += Strcpy(b, hostnames[saved_hosts]);
  585. X        *b++ = '!';
  586. X        }
  587. X        b += Strcpy(b, recipient);
  588. X        if (!strcmp(p2, tmp)) { /* if the same, address was unmodified */
  589. X        b = p2; /* reset offset in buf (b) to where we were (p2) */
  590. X        goto unmodified;
  591. X        }
  592. X        if (*name)
  593. X        b += strlen(sprintf(b, " (%s)", name));
  594. X    } else {
  595. X        char c;
  596. Xunmodified:
  597. X        c = *end;
  598. X        *end = 0;
  599. X        b += Strcpy(b, start); /* copy the entire address with comments */
  600. X        *end = c;
  601. X    }
  602. X    if (b - buf > size) {
  603. X        print("Warning: address list truncated!\n");
  604. X        /* Use a very poor heuristic to find the last complete address */
  605. X        for (b = buf+size - 1; *b != ','; b--)
  606. X        ;
  607. X        print("Lost addresses: %s%s\n", b, end); /* end = not yet parsed */
  608. X        while (isspace(*b) || *b == ',')
  609. X        b--;
  610. X        break;
  611. X    }
  612. X    for (start = end; *start == ',' || isspace(*start); start++)
  613. X        ;
  614. X    if (!*start)
  615. X        break;
  616. X    *b++ = ',', *b++ = ' ', *b = '\0';
  617. X    }
  618. X    (void) strcpy(original, buf);
  619. X}
  620. X
  621. X/*
  622. X * rm_cmts_in_addr() removes the comment lines in addresses that result from
  623. X * sendmail or other mailers which append the user's "real name" on the
  624. X * from lines.  See get_name_n_addr().
  625. X */
  626. Xrm_cmts_in_addr(str)
  627. Xregister char *str;
  628. X{
  629. X    char addr[BUFSIZ], buf[HDRSIZ], *start = str;
  630. X    register char *b = buf;
  631. X
  632. X    *b = 0;
  633. X    do  {
  634. X    if (!(str = get_name_n_addr(str, NULL, addr)))
  635. X        break;
  636. X    b += Strcpy(b, addr);
  637. X    while (*str == ',' || isspace(*str))
  638. X        str++;
  639. X    if (*str)
  640. X        *b++ = ',', *b++ = ' ', *b = '\0';
  641. X    } while (*str);
  642. X    for (b--; b > start && (*b == ',' || isspace(*b)); b--)
  643. X    *b = 0;
  644. X    (void) strcpy(start, buf);
  645. X}
  646. X
  647. X/*
  648. X * take_me_off() is intended to search for the user's login name in an
  649. X * address string and remove it.  If "metoo" is set, return without change.
  650. X * determine which addresses are the "user'"'s addresses by comparing them
  651. X * against the host/path names in alternates.  If the "*" is used, then
  652. X * this matches the address against the user's current login and -any- path.
  653. X *
  654. X * Note that the alternates list is an array of addresses stored *reversed*!
  655. X */
  656. Xtake_me_off(str)
  657. Xchar *str;
  658. X{
  659. X    int i = 0, rm_me;
  660. X    char tmp[256], addr[256], buf[HDRSIZ], *start = str;
  661. X    register char *p, *p2, *b = buf;
  662. X
  663. X    if (!str || !*str || do_set(set_options, "metoo"))
  664. X    return;
  665. X
  666. X    Debug("take_me_off()\n");
  667. X    *b = 0;
  668. X    do  {
  669. X    /* get the first "addres" and advance p to next addres (ignore name) */
  670. X    if (!(p = get_name_n_addr(str, NULL, tmp)))
  671. X        break; /* we've reached the end of the address list */
  672. X    rm_me = FALSE;
  673. X    /* see if user's login is in the address */
  674. X    if (!strcmp(login, tmp))
  675. X        rm_me = TRUE;
  676. X    else {
  677. X        /* put address in !-format and store in "addr" */
  678. X        (void) bang_form(addr, tmp);
  679. X        (void) reverse(addr);
  680. X        for (i = 0; alternates && alternates[i] && !rm_me; i++) {
  681. X        if (alternates[i][0] == '*' && alternates[i][1] == '\0') {
  682. X            (void) strcpy(tmp+1, login), tmp[0] = '!';
  683. X            p2 = reverse(tmp);
  684. X        } else
  685. X            p2 = alternates[i];
  686. X        if (!lcase_strncmp(p2, addr, strlen(p2))) {
  687. X            Debug("\t%s\n", reverse(addr));
  688. X            rm_me = TRUE;
  689. X        }
  690. X        }
  691. X        for (i = 0; !rm_me && ourname && ourname[i]; i++) {
  692. X        p2 = tmp + Strcpy(tmp, ourname[i]);
  693. X        *p2++ = '!';
  694. X        (void) strcpy(p2, login);
  695. X        reverse(tmp);
  696. X        if (!lcase_strncmp(tmp, addr, strlen(tmp))) {
  697. X            Debug("\t%s\n", reverse(addr));
  698. X            rm_me = TRUE;
  699. X        }
  700. X        }
  701. X    }
  702. X    /* The address is not the user's -- put it into the returned list */
  703. X    if (!rm_me) {
  704. X        char c = *p;
  705. X        *p = 0;
  706. X        b += Strcpy(b, str);
  707. X        *p = c;
  708. X    }
  709. X    while (*p == ',' || isspace(*p))
  710. X        p++;
  711. X    if (*p && !rm_me)
  712. X        *b++ = ',', *b++ = ' ', *b = '\0';
  713. X    } while (*(str = p));
  714. X    for (b--; b > start && (*b == ',' || isspace(*b)); b--)
  715. X    *b = 0;
  716. X    (void) strcpy(start, buf);
  717. X}
  718. X
  719. X/*
  720. X * Place commas in between all addresses that don't already have
  721. X * them.  Addresses which use comments which are in parens or _not_
  722. X * within angle brackets *must* already have commas around them or
  723. X * you can't determine what is a comment and what is an address.
  724. X */
  725. Xfix_up_addr(str)
  726. Xchar *str;
  727. X{
  728. X    char buf[HDRSIZ], *start = str;
  729. X    register char c, *p, *b = buf;
  730. X
  731. X    *b = 0;
  732. X    do  {
  733. X    /* get_name returns a pointer to the next address */
  734. X    if (!(p = get_name_n_addr(str, NULL, NULL)))
  735. X        break;
  736. X    c = *p, *p = 0;
  737. X    if (strlen(str) + (b - buf) >= sizeof(buf) - 2) {
  738. X        /* print("Address too long! Lost address: \"%s\"\n", str); */
  739. X        *p = c;
  740. X        break;
  741. X    }
  742. X    for (b += Strcpy(b, str); b > buf && isspace(*(b-1)); b--)
  743. X        *b = 0;
  744. X    for (*p = c; *p == ',' || isspace(*p); p++)
  745. X        ;
  746. X    if (*p)
  747. X        *b++ = ',', *b++ = ' ', *b = '\0';
  748. X    } while (*(str = p));
  749. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  750. X    *b = 0;
  751. X    (void) strcpy(start, buf);
  752. X}
  753. X
  754. X/*
  755. X * Remove redundant addresses.
  756. X * Assume improve_uucp_paths, fix_up_addr or whatever have already been called.
  757. X */
  758. Xrm_redundant_addrs(to, cc)
  759. Xchar *to, *cc;
  760. X{
  761. X    char tmp[256], addr[256], buf[HDRSIZ];
  762. X    char **list; /* a list of addresses for comparison */
  763. X    int list_cnt = 0, l;
  764. X    register char c, *p, *b = buf, *start = to;
  765. X    extern char *calloc();
  766. X
  767. X    Debug("rm_redundant_addrs()\n");
  768. X    list = (char **) calloc(256, sizeof(char *));
  769. X    /* first do the To header */
  770. X    do  {
  771. X    /* get_name returns a pointer to the next address */
  772. X    if (!(p = get_name_n_addr(to, NULL, tmp)))
  773. X        break;
  774. X    c = *p, *p = 0;
  775. X    (void) bang_form(addr, tmp);
  776. X    for (l = 0; l < list_cnt; l++)
  777. X        if (!lcase_strncmp(addr, list[l], -1))
  778. X        break;
  779. X    /* if l == list_cnt, we got a new address, store it and add to buf */
  780. X    if (l == list_cnt) {
  781. X        /* Don't overwrite buffer. */
  782. X        if (list_cnt < 256)
  783. X        list[list_cnt++] = savestr(addr);
  784. X        if (b > buf)
  785. X        *b++ = ',', *b++ = ' ', *b = '\0';
  786. X        for (b += Strcpy(b, to); b > buf && isspace(*(b-1)); b--)
  787. X        *b = 0;
  788. X    } else
  789. X        Debug("\t%s\n", tmp); /* already specified (removed from list) */
  790. X    for (*p = c; *p == ',' || isspace(*p); p++)
  791. X        ;
  792. X    } while (*(to = p));
  793. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  794. X    *b = 0;
  795. X    (void) strcpy(start, buf);
  796. X    b = buf, *b = 0;
  797. X    /* Now do the Cc header.  If addr is listed in the To field, rm it in cc */
  798. X    start = cc;
  799. X    do  {
  800. X    /* get_name returns a pointer to the next address */
  801. X    if (!(p = get_name_n_addr(cc, NULL, tmp)))
  802. X        break;
  803. X    c = *p, *p = 0;
  804. X    (void) bang_form(addr, tmp);
  805. X    for (l = 0; l < list_cnt; l++)
  806. X        if (!lcase_strncmp(addr, list[l], -1))
  807. X        break;
  808. X    if (l == list_cnt) {
  809. X        /* Don't overwrite buffer. */
  810. X        if (list_cnt < sizeof(list)/sizeof(char *))
  811. X        list[list_cnt++] = savestr(addr);
  812. X        if (b > buf)
  813. X        *b++ = ',', *b++ = ' ', *b = '\0';
  814. X        for (b += Strcpy(b, cc); b > buf && isspace(*(b-1)); b--)
  815. X        *b = 0;
  816. X    } else
  817. X        Debug("\t%s\n", tmp); /* already specified (removed from list) */
  818. X    for (*p = c; *p == ',' || isspace(*p); p++)
  819. X        ;
  820. X    } while (*(cc = p));
  821. X    list[list_cnt] = NULL; /* for free_vec */
  822. X    free_vec(list);
  823. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  824. X    *b = 0;
  825. X    (void) strcpy(start, buf);
  826. X}
  827. X
  828. X/*
  829. X * Get address and name from a string (str) which came from an address header
  830. X * in a message or typed by the user.  The string may contain one or more
  831. X * well-formed addresses.  Each must be separated by a comma.
  832. X *
  833. X * address, address, address
  834. X * address (comment or name here)
  835. X * comment or name <address>
  836. X * "Comment, even those with comma's!" <address>
  837. X * address (comma, (more parens), etc...)
  838. X *
  839. X * This does *not* handle cases like:
  840. X *    comment <address (comment)>
  841. X *
  842. X * find the *first* address here and return a pointer to the end of the
  843. X * address (usually a comma).  Return NULL on error: non-matching parens,
  844. X * brackets, quotes...
  845. X */
  846. Xchar *
  847. Xget_name_n_addr(str, name, addr)
  848. Xregister char *str, *name, *addr;
  849. X{
  850. X    register char *p, *p2, *beg_addr = addr, *beg_name = name, c;
  851. X
  852. X    if (addr)
  853. X    *addr = 0;
  854. X    if (name)
  855. X    *name = 0;
  856. X    if (!str || !*str)
  857. X    return NULL;
  858. X
  859. X    /* first check to see if there's something to look for */
  860. X    if (!(p = any(str, ",(<\""))) {
  861. X    /* no comma or indication of a quote character. Find a space and
  862. X     * return that.  If nothing, the entire string is a complete address
  863. X     */
  864. X    while (isspace(*str))
  865. X        str++;
  866. X    if (p = any(str, " \t"))
  867. X        c = *p, *p = 0;
  868. X    if (addr)
  869. X        (void) strcpy(addr, str);
  870. X    if (p)
  871. X        *p = c;
  872. X    return p? p : str + strlen(str);
  873. X    }
  874. X
  875. X    /* comma terminated before any comment stuff.  If so, check for whitespace
  876. X     * before-hand cuz it's possible that strings aren't comma separated yet
  877. X     * and they need to be.
  878. X     *
  879. X     * address address address, address
  880. X     *                        ^p  <- p points here.
  881. X     *        ^p2 <- should point here.
  882. X     */
  883. X    if (*p == ',') {
  884. X    c = *p, *p = 0;
  885. X    if (p2 = any(str, " \t"))
  886. X        *p = ',', c = *p2, p = p2;
  887. X    if (addr)
  888. X        (void) strcpy(addr, str);
  889. X    *p = c;
  890. X    return p;
  891. X    }
  892. X
  893. X    /* starting to get hairy -- we found an angle bracket. This means that
  894. X     * everything outside of those brackets are comments until we find that
  895. X     * all important comma.  A comment AFTER the <addr> :
  896. X     *  <address> John Doe
  897. X     * can't call this function recursively or it'll think that "John Doe"
  898. X     * is a string with two legal address on it (each name being an address).
  899. X     */
  900. X    if (*p == '<') { /* note that "str" still points to comment stuff! */
  901. X    if (name && *str) {
  902. X        *p = 0;
  903. X        name += Strcpy(name, str);
  904. X        *p = '<';
  905. X    }
  906. X    if (!(p2 = index(p+1, '>'))) {
  907. X        wprint("Warning! Malformed address: \"%s\"\n", str);
  908. X        return NULL;
  909. X    }
  910. X    if (addr) {
  911. X        /* to support <addr (comment)> style addresses, add code here */
  912. X        *p2 = 0;
  913. X        skipspaces(1);
  914. X        addr += Strcpy(addr, p);
  915. X        while (addr > beg_addr && isspace(*(addr-1)))
  916. X        *--addr = 0;
  917. X        *p2 = '>';
  918. X    }
  919. X    /* take care of the case "... <addr> com (ment)" */
  920. X    {
  921. X        int p_cnt = 0; /* parenthesis counter */
  922. X        p = p2;
  923. X        /* don't recurse yet -- scan till null, comma or '<'(add to name) */
  924. X        for (p = p2; p[1] && (p_cnt || p[1] != ',' && p[1] != '<'); p++) {
  925. X        if (p[1] == '(')
  926. X            p_cnt++;
  927. X        else if (p[1] == ')')
  928. X            p_cnt--;
  929. X        if (name)
  930. X            *name++ = p[1];
  931. X        }
  932. X        if (p_cnt) {
  933. X        wprint("Warning! Malformed name: \"%s\"\n", name);
  934. X        return NULL;
  935. X        }
  936. X    }
  937. X    if (name && name > beg_name) {
  938. X        while (isspace(*(name-1)))
  939. X        --name;
  940. X        *name = 0;
  941. X    }
  942. X    }
  943. X
  944. X    /* this is the worst -- now we have parentheses/quotes.  These guys can
  945. X     * recurse pretty badly and contain commas within them.
  946. X     */
  947. X    if (*p == '(' || *p == '"') {
  948. X    char *start = p;
  949. X    int comment = 1;
  950. X    c = *p;
  951. X    /* "str" points to address while p points to comments */
  952. X    if (addr && *str) {
  953. X        *p = 0;
  954. X        while (isspace(*str))
  955. X        str++;
  956. X        addr += Strcpy(addr, str);
  957. X        while (addr > beg_addr && isspace(*(addr-1)))
  958. X        *--addr = 0;
  959. X        *p = c;
  960. X    }
  961. X    while (comment) {
  962. X        if (c == '"' && !(p = index(p+1, '"')) ||
  963. X        c == '(' && !(p = any(p+1, "()"))) {
  964. X        wprint("Warning! Malformed address: \"%s\"\n", str);
  965. X        return NULL;
  966. X        }
  967. X        if (*p == '(') /* loop again on parenthesis. quote ends loop */
  968. X        comment++;
  969. X        else
  970. X        comment--;
  971. X    }
  972. X    /* Something like ``Comment (Comment) <addr>''.  In this case
  973. X     * the name should include both comment parts with the
  974. X     * parenthesis.   We have to redo addr.
  975. X     */
  976. X    if ((p2 = any(p+1, "<,")) && *p2 == '<') {
  977. X        if (!(p = index(p2, '>'))) {
  978. X        wprint("Warning! Malformed address: \"%s\"\n", str);
  979. X        return NULL;
  980. X        }
  981. X        if (addr = beg_addr) { /* reassign addr and compare to null */
  982. X        c = *p; *p = 0;
  983. X        addr += Strcpy(addr, p2+1);
  984. X        while (addr > beg_addr && isspace(*(addr-1)))
  985. X            *--addr = 0;
  986. X        *p = c;
  987. X        }
  988. X        if (name) {
  989. X        c = *p2; *p2 = 0;
  990. X        name += Strcpy(name, str);
  991. X        while (name > beg_name && isspace(*(name-1)))
  992. X            *--name = 0;
  993. X        *p2 = c;
  994. X        }
  995. X    } else if (name && start[1]) {
  996. X        c = *p, *p = 0; /* c may be ')' instead of '(' now */
  997. X        name += Strcpy(name, start+1);
  998. X        while (name > beg_name && isspace(*(name-1)))
  999. X        *--name = 0;
  1000. X        *p = c;
  1001. X    }
  1002. X    }
  1003. X    skipspaces(1);
  1004. X    /* this is so common, save time by returning now */
  1005. X    if (!*p || *p == ',')
  1006. X    return p;
  1007. X    return get_name_n_addr(p, name, addr);
  1008. X}
  1009. X
  1010. X/* takes string 's' which can be a name or list of names separated by
  1011. X * commas and checks to see if each is aliased to something else.
  1012. X * return address of the static buf.
  1013. X */
  1014. Xchar *
  1015. Xalias_to_address(s)
  1016. Xregister char *s;
  1017. X{
  1018. X    static char buf[HDRSIZ];
  1019. X    register char *p, *p2, *tmp;
  1020. X    char newbuf[HDRSIZ], c;
  1021. X    static int recursive;
  1022. X
  1023. X    if (!aliases)
  1024. X    return strcpy(buf, s);
  1025. X    if (!s || !*s) {
  1026. X    print("No recipients!?!\n");
  1027. X    return NULL;
  1028. X    }
  1029. X    if (!recursive) {
  1030. X    bzero(buf, sizeof buf);
  1031. X    p2 = buf;  /* if we're starting all this, p2 starts at &buf[0] */
  1032. X    } else
  1033. X    p2 = buf+strlen(buf);   /* else, pick up where we left off */
  1034. X
  1035. X    if (++recursive == 30) {
  1036. X    print("alias references too many addresses!\n");
  1037. X    recursive = 0;
  1038. X    return NULL;
  1039. X    }
  1040. X    do  {
  1041. X    char addr[256];
  1042. X    if (!(p = get_name_n_addr(s, NULL, addr)))
  1043. X        break;
  1044. X    c = *p, *p = 0;
  1045. X
  1046. X    /* On recursive calls, compare against the entire
  1047. X     * previous expansion, not just the address part.
  1048. X     */
  1049. X    if (recursive > 1)
  1050. X        (void) strcpy(addr, s);
  1051. X
  1052. X    /* if this is an alias, recurse this routine to expand it out */
  1053. X    if ((tmp = do_set(aliases, addr)) && *tmp) {
  1054. X        if (!alias_to_address(strcpy(newbuf, tmp))) {
  1055. X        *p = c;
  1056. X        return NULL;
  1057. X        } else
  1058. X        p2 = buf+strlen(buf);
  1059. X    /* Now, make sure the buffer doesn't overflow */
  1060. X    } else if (strlen(s) + (p2-buf) + 2 > sizeof buf) {  /* add ", " */
  1061. X        print("address length too long.\n");
  1062. X        recursive = 0;
  1063. X        *p = c;
  1064. X        return NULL;
  1065. X    } else {
  1066. X        /* append the new alias (or unchanged address) onto the buffer */
  1067. X        p2 += Strcpy(p2, s);
  1068. X        *p2++ = ',', *p2++ = ' ', *p2 = '\0';
  1069. X    }
  1070. X    for (*p = c; *p == ',' || isspace(*p); p++)
  1071. X        ;
  1072. X    } while (*(s = p));
  1073. X    if (recursive)
  1074. X    recursive--;
  1075. X    if (!recursive)
  1076. X    *(p2-2) = 0;  /* get rid of last ", " if end of recursion */
  1077. X    return buf;
  1078. X}
  1079. X
  1080. X/*
  1081. X * Wrap addresses so that the headers don't exceed n chars (typically 80).
  1082. X */
  1083. Xchar *
  1084. Xwrap_addrs(str, n)
  1085. Xchar *str;
  1086. X{
  1087. X    char buf[HDRSIZ * 2], *start = str;
  1088. X    register char *b = buf, *p, c, *line_start = buf;
  1089. X
  1090. X    *b = 0;
  1091. X    do  {
  1092. X    /* get_name returns a pointer to the next address */
  1093. X    if (!(p = get_name_n_addr(str, NULL, NULL)))
  1094. X        break;
  1095. X    c = *p, *p = 0;
  1096. X    if (b > buf) {
  1097. X        *b++ = ',', *b++ = ' ', *b = '\0';
  1098. X        if (b - line_start + strlen(str) + 8 /* \t = 8 */ >= n)
  1099. X        *b++ = '\n', *b++ = '\t', line_start = b;
  1100. X    }
  1101. X    for (b += Strcpy(b, str); b > buf && isspace(*(b-1)); b--)
  1102. X        *b = 0;
  1103. X    for (*p = c; *p == ',' || isspace(*p); p++)
  1104. X        ;
  1105. X    } while (*(str = p));
  1106. X    for (b--; b > buf && (*b == ',' || isspace(*b)); b--)
  1107. X    *b = 0;
  1108. X    return strcpy(start, buf);
  1109. X}
  1110. END_OF_FILE
  1111. if test 31712 -ne `wc -c <'addrs.c'`; then
  1112.     echo shar: \"'addrs.c'\" unpacked with wrong size!
  1113. fi
  1114. # end of 'addrs.c'
  1115. fi
  1116. echo shar: End of archive 15 \(of 19\).
  1117. cp /dev/null ark15isdone
  1118. MISSING=""
  1119. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
  1120.     if test ! -f ark${I}isdone ; then
  1121.     MISSING="${MISSING} ${I}"
  1122.     fi
  1123. done
  1124. if test "${MISSING}" = "" ; then
  1125.     echo You have unpacked all 19 archives.
  1126.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1127. else
  1128.     echo You still need to unpack the following archives:
  1129.     echo "        " ${MISSING}
  1130. fi
  1131. ##  End of shell archive.
  1132. exit 0
  1133.